/*
 *  Copyright (c)  13.04.2007 Philipp Marx.
 *  Permission is granted to copy, distribute and/or modify this document
 *  under the terms of the GNU Free Documentation License, Version 1.2
 *  or any later version published by the Free Software Foundation;
 *  with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
 *  A copy of the license is included in the section entitled "GNU
 *  Free Documentation License".
 */

package de.smigfu.irc.handler;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import de.smigfu.irc.core.IRCProtocolConstants;
import de.smigfu.irc.core.IRCServerConnection;

/**
 * A <code>ChannelUserMessageHandler</code> is a <code>MessageHandler</code> which informs about
 * users in a channel. It provides callbacks for:<br><br>
 * 
 * <li>JOIN</li>
 * <li>PART</li>
 * <li>OP/DEOP</li>
 * <li>VOICE/DEVOICE</li>
 * <li>NAMES</li>
 * <li>NICK</li>
 * <li>QUIT</li>
 * 
 * <br>
 *
 * @version $Revision$ $Date$
 *
 * @author Philipp Marx, last modified by $Author$
 */
public abstract class ChannelUserMessageHandler implements MessageHandler {

   /** 
    * A list containg all the names which are transmitted during a 
    * <code>IRCProtocolConstants.RPL_NAMREPLY</code>. 
    */
   private List<String> tmpNickList;

   /**
    * Constructs a <code>ChannelUserMessageHandler</code>.
    *
    */
   public ChannelUserMessageHandler() {
      this.tmpNickList = new ArrayList<String>();
   }

   /* (non-Javadoc)
    * @see de.smigfu.irc.handler.MessageHandler#handleMessageReceived(de.smigfu.irc.core.IRCServerConnection, de.smigfu.irc.handler.Message)
    */
   public void handleMessageReceived(final IRCServerConnection connection, final Message message) {
      if (message.getCommand().equals(IRCProtocolConstants.CMD_JOIN)) {
         final String nickName = message.getNickName();
         final String params = message.getParams();
         final int channelStart = params.indexOf("#");
         final String channel = params.substring(channelStart);
         this.handleJoinReceived(connection, message, nickName, channel);
      } else if (message.getCommand().equals(IRCProtocolConstants.CMD_PART)) {
         final String nickName = message.getNickName();
         final String params = message.getParams();
         final int channelStart = params.indexOf("#");
         final String channel = params.substring(channelStart);
         this.handlePartReceived(connection, message, nickName, channel);
      } else if (message.getCommand().equals(IRCProtocolConstants.RPL_NAMREPLY)) {
         final String params = message.getParams();
         final String[] nicks = params.substring(params.indexOf(":") + 1).split(" ");

         this.tmpNickList.addAll(Arrays.asList(nicks));
      } else if (message.getCommand().equals(IRCProtocolConstants.RPL_ENDOFNAMES)) {
         final String params = message.getParams();
         final int channelStart = params.indexOf("#");
         final int channelEnd = params.indexOf(" ", channelStart);
         final String channel = params.substring(channelStart, channelEnd);

         this.handleNickNameListReceived(connection,
                                         new ArrayList<String>(this.tmpNickList),
                                         channel);

         this.tmpNickList.clear();
      } else if (message.getCommand().equals(IRCProtocolConstants.CMD_MODE)) {
         String params = message.getParams();
         final int channelStart = params.indexOf("#");
         final int channelEnd = params.indexOf(" ", channelStart);
         final String channel = params.substring(channelStart, channelEnd);

         params = params.substring(channelEnd + 1);
         final String[] modeCommands = params.split("(\\+|-)");
         int currentSignIndex = 0;
         for (int i = 1; i < modeCommands.length; i++) { // Start at 1 because the first element will be an empty one.
            final String[] modeCommandParts = modeCommands[i].split(" ");
            for (int j = 0; j < modeCommandParts[0].length(); j++) {
               if (modeCommandParts[0].charAt(j) == 'o') {
                  final String nickName = modeCommandParts[j + 1];
                  final char sign = params.charAt(currentSignIndex);
                  if (sign == '+') {
                     this.handleOpReceived(connection, message, nickName, channel);
                  } else if (sign == '-') {
                     this.handleDeopReceived(connection, message, nickName, channel);
                  }
               } else if (modeCommandParts[0].charAt(j) == 'v') {
                  final String nickName = modeCommandParts[j + 1];
                  final char sign = params.charAt(currentSignIndex);
                  if (sign == '+') {
                     this.handleVoiceReceived(connection, message, nickName, channel);
                  } else if (sign == '-') {
                     this.handleDevoiceReceived(connection, message, nickName, channel);
                  }
               }
            }
            currentSignIndex += modeCommands[i].length();
         }
      } else if (message.getCommand().equals(IRCProtocolConstants.CMD_NICK)) {
         final String nickName = message.getNickName();
         final String params = message.getParams();
         final String newNickName = params.substring(params.indexOf(":") + 1);

         this.handleNickReceived(connection, message, nickName, newNickName);
      } else if (message.getCommand().equals(IRCProtocolConstants.CMD_QUIT)) {
         final String nickName = message.getNickName();
         this.handleQuitReceived(nickName);
      }
   }

   /* (non-Javadoc)
    * @see de.smigfu.irc.handler.MessageHandler#handleMessageSent(de.smigfu.irc.core.IRCServerConnection, de.smigfu.irc.handler.Message)
    */
   public void handleMessageSent(final IRCServerConnection connection, final Message message) {
      // noop
   }

   /**
    * This method is callbacked on a "Deop"-Operation.
    * 
    * @param connection The <code>IRCConnection</code>.
    * @param message The whole message.
    * @param nickName The nickname involved.
    * @param channel The channel involved.
    */
   protected abstract void handleDeopReceived(IRCServerConnection connection,
                                              Message message,
                                              String nickName,
                                              String channel);

   /**
    * This method is callbacked on a "Devoice"-Operation.
    * 
    * @param connection The <code>IRCConnection</code>.
    * @param message The whole message.
    * @param nickName The nickname involved.
    * @param channel The channel involved.
    */
   protected abstract void handleDevoiceReceived(IRCServerConnection connection,
                                                 Message message,
                                                 String nickName,
                                                 String channel);

   /**
    * This method is callbacked on a "Deop"-Operation.
    * 
    * @param connection The <code>IRCConnection</code>.
    * @param message The whole message.
    * @param nickName The nickname involved.
    * @param channel The channel involved.
    */
   protected abstract void handleJoinReceived(IRCServerConnection connection,
                                              Message message,
                                              String nickName,
                                              String channel);

   /**
    * This method is callbacked on a receiption of a list with nicknames for a channel.
    * 
    * @param connection The <code>IRCConnection</code>.
    * @param nickNameList The list with the nicknames.
    * @param channel The channel involved.
    */
   protected abstract void handleNickNameListReceived(IRCServerConnection connection,
                                                      List<String> nickNameList,
                                                      String channel);

   /**
    * This method is callbacked on a receipition of <code>IRCProtocolConstants.CMD_NICK</code>.
    * 
    * @param connection The <code>IRCConnection</code>.
    * @param message The whole message.
    * @param oldNickName The old nickname.
    * @param newNickName The new nickname.
    */
   protected abstract void handleNickReceived(IRCServerConnection connection,
                                              Message message,
                                              String oldNickName,
                                              String newNickName);

   /**
    * This method is callbacked on a "Op"-Operation.
    * 
    * @param connection The <code>IRCConnection</code>.
    * @param message The whole message.
    * @param nickName The nickname involved.
    * @param channel The channel involved.
    */
   protected abstract void handleOpReceived(IRCServerConnection connection,
                                            Message message,
                                            String nickName,
                                            String channel);

   /**
    * This method is callbacked on a receipition of <code>IRCProtocolConstants.CMD_PART</code>.
    * 
    * @param connection The <code>IRCConnection</code>.
    * @param message The whole message.
    * @param nickName The nickname involved.
    * @param channel The channel involved.
    */
   protected abstract void handlePartReceived(IRCServerConnection connection,
                                              Message message,
                                              String nickName,
                                              String channel);

   /**
    * This method is callbacked on a receipition of <code>IRCProtocolConstants.CMD_QUIT</code>.
    * 
    * @param nickName The nickname involved.
    */
   protected abstract void handleQuitReceived(String nickName);

   /**
    * This method is callbacked on a "Voice"-Operation.
    * 
    * @param connection The <code>IRCConnection</code>.
    * @param message The whole message.
    * @param nickName The nickname involved.
    * @param channel The channel involved.
    */
   protected abstract void handleVoiceReceived(IRCServerConnection connection,
                                               Message message,
                                               String nickName,
                                               String channel);
}
